import taichi as ti
from ..camera import Camera
from ..utils import *


class Parallel(Camera):
    """正交投影相机"""

    def __init__(self, size: "tuple[int, int]", width: float = 1) -> None:
        super().__init__(size)
        self.width: ti.ScalarField = ti.field(float, shape=1)
        self.width[0] = width
        self.pixelW: ti.MatrixField = Vec3.field(shape=1)
        self.pixelH: ti.MatrixField = Vec3.field(shape=1)
        self.pixelWH: ti.MatrixField = Mat3x2.field(shape=1)
        self.offset: ti.MatrixField = Vec3.field(shape=1)
        self.lookAt: ti.MatrixField = Vec3.field(shape=1)
        self.setScreen()

    def setScreen(self):
        sinTheta = ti.sin(self.theta)
        self.lookAt[0] = Vec3(
            ti.cos(self.phi) * sinTheta, ti.cos(self.theta), ti.sin(self.phi) * sinTheta
        )
        x = Vec3(ti.cos(self.phi - PI / 2), 0, ti.sin(self.phi - PI / 2))
        y = self.lookAt[0].cross(x)
        self.x, self.y, self.z = Vec4(x, 0), Vec4(y, 0), Vec4(self.lookAt[0], 0)
        self.pixelH[0] = 2 * y / self.h / self.aspectRatio
        self.pixelW[0] = 2 * x / self.w
        self.pixelWH[0] = Mat2x3(self.pixelW[0], self.pixelH[0]).transpose()
        self.offset[0] = -x - y / self.aspectRatio
        return self

    @ti.func
    def rayAt(self, uv: Vec2, t: int = 0) -> Ray4:
        ss = superSampling(self.SSNumber, t)
        return Ray4(
            Vec4(
                self.origin[0].xyz
                + (self.offset[0] + self.pixelWH[0] @ (uv + ss)) * self.width[0],
                self.origin[0].w,
            ),
            Vec4(self.lookAt[0].normalized(), -1),
        )

    def makeSettingPlane(self, gui: ti.ui.Gui) -> bool:
        clear = False
        if (
            new_value := gui.slider_float("width", self.width[0], 1e-3, 1e2)
        ) != self.width[0]:
            self.width[0] = new_value
            clear = True
        if clear:
            self.setScreen()
        return clear
